//    pfx2particles - converts Maya PaintEffects to Particles.
//    Copyright (C) 2008  Sergey Tsyptsyn & Paolo Berto, /*jupiter jazz*/
//
//    info@jupiter-jazz.com
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.


////////////////////////////////////////////////////////////////////////
//  Syntax:
//  	pfx2Particles $pfxNodeName;
//
////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <assert.h>
#include <maya/MIOStream.h>

#include <maya/MPxCommand.h>
#include <maya/MFnPlugin.h>
#include <maya/MString.h>
#include <maya/MArgList.h>

#include <maya/MFnCamera.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>
#include <maya/MDagPath.h>

#include <maya/MObject.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MVectorArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MSelectionList.h>
#include <maya/MFnDagNode.h>
#include <maya/MRenderLine.h>
#include <maya/MRenderLineArray.h>
#include <maya/MFnPfxGeometry.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>

#include <maya/MPointArray.h>
#include <maya/MFloatMatrix.h>
#include <maya/MItDag.h>

#include <maya/MFnParticleSystem.h>
#include <maya/MObjectArray.h>
#include <maya/MTime.h>
#include <maya/MDagModifier.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnNumericAttribute.h>

#include <maya/MFnData.h>
#include <maya/MFnNumericData.h>
#include <maya/MFnNurbsCurveData.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MFnTransform.h>
#include <maya/MTransformationMatrix.h>
#include <maya/MMatrix.h>
#include <maya/MFloatMatrix.h>
#include <maya/MArgDatabase.h>
#include <maya/MArgParser.h>
#include <maya/MSyntax.h>

#define CHECKRESULT(stat,msg)     \
        if ( MS::kSuccess != stat ) { \
                MGlobal::displayInfo( msg ); return MS::kFailure;     \
        }

#define mCommandName "pfx2Particles"	// Command name

class pfx2ParticlesCmd : public MPxCommand
{
public:
				 pfx2ParticlesCmd();
    virtual		~pfx2ParticlesCmd();

    virtual MStatus doIt ( const MArgList& args );
	static MSyntax newSyntax();

    static void* creator();

private:

	static MStatus nodeFromName(MString name, MObject & obj);

	MStatus parseArgs ( const MArgList& args );

	MStatus printRenderLine( MRenderLine &renderLine, int lineType  );
	MStatus printRenderLineArray( MRenderLineArray &lineArray, int lineType  );
	MString pfxName;
	MString particleName;
	MString cameraName;
	MString nodeType;
	
	MObject pfxNode;
	MObject particleNode;
	MObject cameraNode;
	int pfxOutput;
	double  pfxStampDensity;
	float pfxRadius; 
	double  radiusThreshold;
	bool velocityOutput;
	bool saveInitialState;
	bool useFlantess;
	bool useWidthMultiplication;
	bool shortOutput;
	bool spriteSupport;
	
			
	MVector cameraPos;
	MMatrix cameraMatrix;
	MFloatMatrix pMatrix;	
	MVectorArray posArray;
	MVectorArray colArray;
	MVectorArray twiArray;
	MVectorArray incArray;
	MVectorArray traArray;
	MVectorArray velArray;
	MDoubleArray opaArray;
	MDoubleArray widArray;
	MDoubleArray flaArray;
	MDoubleArray parArray;
	MDoubleArray typArray;
	MDoubleArray rotArray;
	MDoubleArray uDeltaArray;

	//MDoubleArray result; 
};

pfx2ParticlesCmd::pfx2ParticlesCmd()
{
}

pfx2ParticlesCmd::~pfx2ParticlesCmd() 
{
}

///////////////////////////////////////////////////////////////////////////////

MStatus pfx2ParticlesCmd::nodeFromName(MString name, MObject & obj)
{
	MSelectionList tempList;
    tempList.add( name );
    if ( tempList.length() > 0 ) 
	{
		tempList.getDependNode( 0, obj );
		return MS::kSuccess;
	}
	return MS::kFailure;
}

MSyntax pfx2ParticlesCmd::newSyntax() {
	MSyntax syntax;
	syntax.addArg( MSyntax::kString );
	syntax.addFlag( "-p", "-particleShape", MSyntax::kString );				// pfxName + particleShape by default
	syntax.addFlag( "-o", "-objectType", MSyntax::kString );				// "particle" by default
	syntax.addFlag( "-u", "-update", MSyntax::kLong );						// 1 by default; 0 - only count; 2,3 -future use
	syntax.addFlag( "-r", "-radiusThreshold", MSyntax::kDouble );			// 0.001 by default - skip smaller stamps, decrease number of points
	syntax.addFlag( "-v", "-velocityOutput", MSyntax::kBoolean );			// false by default - fill velocities with point-to-nextpoint vector - for streaks
	syntax.addFlag( "-s", "-saveInitialState", MSyntax::kBoolean );			// true by default
	syntax.addFlag( "-f", "-useFlantess", MSyntax::kBoolean );				// false by default - calculate and use width modulation depending on camera view
	syntax.addFlag( "-m", "-useWidthMultiplication", MSyntax::kBoolean );	// true by default - mult radiusPP with radius, pointSize or spriteScaleX
	syntax.addFlag( "-so", "-shortOutput", MSyntax::kBoolean );			// false by default - output only positions, radiusPP, rgbPP, opacPP, incanPP
	syntax.addFlag( "-ss", "-spriteSupport", MSyntax::kBoolean );			// true by default - false suppresses sprite calculations and attr
	return syntax;
}
 
MStatus pfx2ParticlesCmd::parseArgs( const MArgList& args )
{
	// Parse the arguments.
	MStatus stat = MS::kSuccess;
	char buffer[256];
	MArgDatabase argData(syntax(), args);
	pfxOutput = 1; //update particle positions by default
	pfxRadius = 1;
	radiusThreshold=0.001;

	pfxName = argData.commandArgumentString( 0, &stat);
	CHECKRESULT(stat,"getting pfx stroke failed!");
	if( stat != MS::kSuccess )
	{
		MGlobal::displayError( "getting pfx stroke failed !" );
		return MS::kFailure;
	}

	if (argData.isFlagSet("-p"))
	{
		particleName = argData.flagArgumentString("-p", 0, &stat);
		CHECKRESULT(stat,"-p failed!");
	}
	else particleName = pfxName + MString("ParticleShape");

	if (argData.isFlagSet("-o"))
	{
		nodeType = argData.flagArgumentString("-o", 0, &stat);
		CHECKRESULT(stat,"-o failed!");
	}
	else nodeType = MString("particle");

	if (argData.isFlagSet("-u"))
	{
		pfxOutput = argData.flagArgumentInt("-u", 0, &stat);
		CHECKRESULT(stat,"-update failed!");
	}
	else pfxOutput = 1;

	if (argData.isFlagSet("-r"))
	{
		radiusThreshold = argData.flagArgumentDouble("-r", 0, &stat);
		CHECKRESULT(stat,"-radiusThreshold failed!");
	}
	else radiusThreshold = 0.001;

	if (argData.isFlagSet("-v"))
	{
		velocityOutput = argData.flagArgumentBool("-v", 0, &stat);
		CHECKRESULT(stat,"-velocityOutput failed!");
	}
	else velocityOutput = false;

	if (argData.isFlagSet("-s"))
	{
		saveInitialState = argData.flagArgumentBool("-s", 0, &stat);
		CHECKRESULT(stat,"-saveInitialState failed!");
	}
	else saveInitialState = true;

	if (argData.isFlagSet("-f"))
	{
		useFlantess = argData.flagArgumentBool("-f", 0, &stat);
		CHECKRESULT(stat,"-useFlantess failed!");
	}
	else useFlantess = false;

	if (argData.isFlagSet("-ss"))
	{
		spriteSupport = argData.flagArgumentBool("-ss", 0, &stat);
		CHECKRESULT(stat,"-spriteSupport failed!");
	}
	else spriteSupport = true;

	if (argData.isFlagSet("-m"))
	{
		useWidthMultiplication = argData.flagArgumentBool("-m", 0, &stat);
		CHECKRESULT(stat,"-useWidthMultiplication failed!");
	}
	else useWidthMultiplication = true;

	if (argData.isFlagSet("-so"))
	{
		shortOutput = argData.flagArgumentBool("-so", 0, &stat);
		CHECKRESULT(stat,"-shortOutput failed!");
	}
	else shortOutput = false;


	//sprintf( buffer, "node name %s, %s  ", pfxName.asChar(), particleName.asChar() );
	//MGlobal::displayInfo( buffer );
	
	////////////////////////////////////////////////////////////////////////
	//getting stroke
	////////////////////////////////////////////////////////////////////////
	nodeFromName( pfxName, pfxNode );
	if( pfxNode.isNull() )
	{
		MGlobal::displayError( "There is no pfx node with the given name." );
		return MS::kFailure;
	}

	if( ! (pfxNode.hasFn( MFn::kPfxGeometry )
	   || pfxNode.hasFn( MFn::kStroke ) 
	   || pfxNode.hasFn( MFn::kPfxHair )) )
	{
		MGlobal::displayError( "The named node is not a stroke or pfxHair node." );
		return MS::kFailure;
	}
	
	//getting density
	pfxStampDensity=1;
	MFnDependencyNode fnStroke(pfxNode);
	MObject attr = fnStroke.attribute("brush",&stat);
	MPlug attrPlug = MPlug(  pfxNode , attr );
	MPlugArray connPlugs;
	attrPlug.connectedTo ( connPlugs , true, true, &stat );
	unsigned connLength = connPlugs.length();
	for (unsigned ii = 0; ii < connLength; ++ii) {
			if (connPlugs[ii].node().apiType() == MFn::kBrush) {
				MObject brushNode = connPlugs[ii].node();
				MFnDependencyNode fnBrush(brushNode);
				MObject attrStamp = fnBrush.attribute("stampDensity",&stat);
				MPlug attrStampPlug = MPlug(  brushNode , attrStamp );
				attrStampPlug.getValue(pfxStampDensity);
				//sprintf( buffer, "%s    stamps = %f", attrStampPlug.name().asChar(), pfxStampDensity);
				//MGlobal::displayInfo( buffer );
				}
			}
	///////////////////////////////////////////////////////////////////////////////////////
	//getting particle
	///////////////////////////////////////////////////////////////////////////////////////
	
	if(pfxOutput==0) return MS::kSuccess; //just count the stamps - no output to particle
	
	nodeFromName( particleName, particleNode );
	
	//creating new particle
	if( particleNode.isNull() )
	{
		// need to create particle node
//		MFnParticleSystem dummy;
//		particleNode = dummy.create(&stat);
//		MDagModifier dummy;//(particleNode);
/*		MObject parent;
		particleNode = dummy.create( &stat );
		
		MDagPath dag, dagT;
		stat = dag.getAPathTo(particleNode, dagT);
		CHECKRESULT(stat,"DAG.getApath failed!");
		parent = dagT.transform(&stat);
		CHECKRESULT(stat,"DAG.transform failed!");

		MFnDagNode fnDag(parent);
		particleNodefnDag.child(0, &stat);
		CHECKRESULT(stat,"DAG.child failed!");
*/
//
		MFnDependencyNode fnDn;
		MObject parent = fnDn.create( nodeType ,  particleName, &stat ); //dummy.create();
		CHECKRESULT(stat,"fnDn.create(MString(particle)... failed!");

		MFnDagNode fnDag(parent);
		particleNode = fnDag.child(0, &stat);
		CHECKRESULT(stat,"DAG.child failed!");
		
		MFnParticleSystem ps(particleNode, &stat);
		CHECKRESULT(stat,"MFnParticleSystem ps failed!");
		
		MPlug timePlug = ps.findPlug(MString("currentTime"), false, &stat); 
		CHECKRESULT(stat,"findPlug failed!");
		
		MObject time1;
		nodeFromName( MString("time1"), time1 );
		fnDn.setObject(time1); 
		MPlug outTimePlug = fnDn.findPlug(MString("outTime"), false, &stat); 
		
		MDGModifier dgModifier;
		dgModifier.connect(outTimePlug, timePlug);
        dgModifier.doIt();


		
	//MString str = ps.particleName();
	//sprintf( buffer, "new Particle %s ", str.asChar() );
	//MGlobal::displayInfo( buffer );
	
		MFnTypedAttribute fnAttr;
		MFnNumericAttribute nnAttr;
		//radius as mult for width
		ps.addAttribute( nnAttr.create( MString( "radius" ), MString( "radius" ), MFnNumericData::kFloat, 1.0), MFnDependencyNode::kLocalDynamicAttr );
		ps.addAttribute( nnAttr.create( MString( "pointSize" ), MString( "pointSize" ), MFnNumericData::kFloat, 1.0), MFnDependencyNode::kLocalDynamicAttr );
		
		//MObject rgbAttr = fnAttr.create( MString( "rgbPP" ), MString( "rgbPP" ), MFnData::kVectorArray);
		ps.addAttribute( fnAttr.create( MString( "rgbPP" ), MString( "rgbPP" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
		ps.addAttribute( fnAttr.create( MString( "rgbPP0" ), MString( "rgbPP0" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
		
		//MObject radAttr = fnAttr.create( MString( "radiusPP" ), MString( "radiusPP" ), MFnData::kDoubleArray);
		ps.addAttribute( fnAttr.create( MString( "radiusPP" ), MString( "radiusPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
		ps.addAttribute( fnAttr.create( MString( "radiusPP0" ), MString( "radiusPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
		
		//MObject opacityAttr = fnAttr.create( MString( "opacityPP" ), MString( "opacityPP" ), MFnData::kDoubleArray);
		ps.addAttribute( fnAttr.create( MString( "opacityPP" ), MString( "opacityPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
		ps.addAttribute( fnAttr.create( MString( "opacityPP0" ), MString( "opacityPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
		
		//MObject incandescenceAttr = fnAttr.create( MString( "incandescencePP" ), MString( "incandescencePP" ), MFnData::kVectorArray);
		ps.addAttribute( fnAttr.create( MString( "incandescencePP" ), MString( "incandescencePP" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
		ps.addAttribute( fnAttr.create( MString( "incandescencePP0" ), MString( "incandescencePP0" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
		
		if(spriteSupport) {
			//MObject spriteScaleXAttr = fnAttr.create( MString( "spriteScaleXPP" ), MString( "spriteScaleXPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "spriteScaleXPP" ), MString( "spriteScaleXPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "spriteScaleXPP0" ), MString( "spriteScaleXPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject spriteScaleYAttr = fnAttr.create( MString( "spriteScaleYPP" ), MString( "spriteScaleYPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "spriteScaleYPP" ), MString( "spriteScaleYPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "spriteScaleYPP0" ), MString( "spriteScaleYPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject spriteScaleYAttr = fnAttr.create( MString( "spriteTwistPP" ), MString( "spriteTwistPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "spriteTwistPP" ), MString( "spriteTwistPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "spriteTwistPP0" ), MString( "spriteTwistPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			}
			// customPP----------------------------------------------------
			/*
			//MObject pfxStampAttr = fnAttr.create( MString( "pfxStampPP" ), MString( "pfxStampPP" ), MFnData::kVectorArray);
			ps.addAttribute( fnAttr.create( MString( "pfxStampPP" ), MString( "pfxStampPP" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxStampPP0" ), MString( "pfxStampPP0" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxrgbAttr = fnAttr.create( MString( "pfxrgbPP" ), MString( "pfxrgbPP" ), MFnData::kVectorArray);
			ps.addAttribute( fnAttr.create( MString( "pfxrgbPP" ), MString( "pfxrgbPP" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxrgbPP0" ), MString( "pfxrgbPP0" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxradAttr = fnAttr.create( MString( "pfxradiusPP" ), MString( "pfxradiusPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "pfxradiusPP" ), MString( "pfxradiusPP" ), MFnData::kDoubleArray),	MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxradiusPP0" ), MString( "pfxradiusPP0" ), MFnData::kDoubleArray),	MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxtransparencyAttr = fnAttr.create( MString( "pfxtransparencyPP" ), MString( "pfxtransparencyPP" ), MFnData::kVectorArray);
			ps.addAttribute( fnAttr.create( MString( "pfxtransparencyPP" ), MString( "pfxtransparencyPP" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxtransparencyPP0" ), MString( "pfxtransparencyPP0" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxincandescenceAttr = fnAttr.create( MString( "pfxincandescencePP" ), MString( "pfxincandescencePP" ), MFnData::kVectorArray);
			ps.addAttribute( fnAttr.create( MString( "pfxincandescencePP" ), MString( "pfxincandescencePP" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxincandescencePP0" ), MString( "pfxincandescencePP0" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
	
			//MObject pfxspriteScaleXAttr = fnAttr.create( MString( "pfxspriteScaleXPP" ), MString( "pfxspriteScaleXPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "pfxspriteScaleXPP" ), MString( "pfxspriteScaleXPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxspriteScaleXPP0" ), MString( "pfxspriteScaleXPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxspriteScaleYAttr = fnAttr.create( MString( "pfxspriteScaleYPP" ), MString( "pfxspriteScaleYPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "pfxspriteScaleYPP" ), MString( "pfxspriteScaleYPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxspriteScaleYPP0" ), MString( "pfxspriteScaleYPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
	*/		
	
		if(!shortOutput) {
			//MObject pfxtwistAttr = fnAttr.create( MString( "pfxtwistPP" ), MString( "pfxtwistPP" ), MFnData::kVectorArray);
			ps.addAttribute( fnAttr.create( MString( "pfxNormalPP" ), MString( "pfxNormalPP" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxNormalPP0" ), MString( "pfxNormalPP0" ), MFnData::kVectorArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxflatnessAttr = fnAttr.create( MString( "pfxflatnessPP" ), MString( "pfxflatnessPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "pfxFlatnessPP" ), MString( "pfxFlatnessPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxFlatnessPP0" ), MString( "pfxFlatnessPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxparameterAttr = fnAttr.create( MString( "pfxparameterPP" ), MString( "pfxparameterPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "pfxParameterPP" ), MString( "pfxParameterPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxParameterPP0" ), MString( "pfxParameterPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxtypeAttr = fnAttr.create( MString( "pfxDeltaUPP" ), MString( "pfxDeltaUPP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "pfxDeltaUPP" ), MString( "pfxDeltaUPP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxDeltaUPP0" ), MString( "pfxDeltaUPP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			
			//MObject pfxtypeAttr = fnAttr.create( MString( "pfxtypePP" ), MString( "pfxtypePP" ), MFnData::kDoubleArray);
			ps.addAttribute( fnAttr.create( MString( "pfxTypePP" ), MString( "pfxTypePP" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			ps.addAttribute( fnAttr.create( MString( "pfxTypePP0" ), MString( "pfxTypePP0" ), MFnData::kDoubleArray), MFnDependencyNode::kLocalDynamicAttr );
			}  
		
	}


	if( ! (particleNode.hasFn( MFn::kParticle ) ) )
	{
		MGlobal::displayError( "The named node is not a particleNode." );
		return MS::kFailure;
	}
	//checking particle type
	MFnParticleSystem ps( particleNode, &stat );
	CHECKRESULT(stat,"MFnParticleSystem::MFnParticleSystem(MObject,status) failed!");
	if( ! ps.isValid() )
	{
		MGlobal::displayError( "The particleNode function set is invalid!" );
		return MS::kFailure;
	}
	
		
	MFnParticleSystem::RenderType pType = ps.renderType();
		//radius
	MString attrName;
	if(pType == MFnParticleSystem::kPoints || pType == MFnParticleSystem::kMultiPoint || pType == MFnParticleSystem::kNumeric) attrName = "pointSize";
	if(pType == MFnParticleSystem::kSpheres || pType == MFnParticleSystem::kBlobby || pType == MFnParticleSystem::kCloud  ) attrName = "radius";
	//if(pType == MFnParticleSystem::kStreak || pType == MFnParticleSystem::kMultiStreak) attrName = "lineWidth";
	if(pType == MFnParticleSystem::kSprites ) attrName = "spriteScaleX";
	if(ps.hasAttribute(attrName) ) {
		MObject attrRadiusMult = ps.attribute(attrName,&stat);
		CHECKRESULT(stat,"attrRadiusMult failed!");
		//get plug for radius mult
		MPlug attrRadiusMultPlug = MPlug(  particleNode , attrRadiusMult );
		attrRadiusMultPlug.getValue(pfxRadius);
     	}
	else pfxRadius = 1;  // no attribute to mult

	////////////////////////////////////////////////////////////////////////////////////////////

	MDagPath path;
	
	stat = M3dView::active3dView().getCamera(path);	
	CHECKRESULT(stat,"getCamera failed!");
	
	MFnCamera fnCamera(path, &stat);
	CHECKRESULT(stat,"MFncamera failed!");
	pMatrix = fnCamera.projectionMatrix();
/*	stat = fnCamera.getPath(path);
	CHECKRESULT(stat,"path failed!");
*/	MObject trans = path.transform(&stat);
	CHECKRESULT(stat,"tranform failed!");
	MFnTransform fnTr(trans,&stat);
	CHECKRESULT(stat,"MFnTr failed!");
	cameraPos = fnTr.getTranslation(MSpace::kTransform, &stat );
	CHECKRESULT(stat,"getTranslation failed!");
	
	MTransformationMatrix tMatrix = fnTr.transformation( &stat );
	cameraMatrix = tMatrix.asMatrixInverse();
	CHECKRESULT(stat,"get mMatrix failed!");
	
	//MPoint cPos = MVector(1,0,0) * mMatrix;
	
	//sprintf( buffer, "cam pos  %f %f %f", cPos.x,  cPos.y,  cPos.z );
	//MGlobal::displayInfo( buffer );
	//sprintf( buffer, "cam pos  %f %f %f", cameraPos.x,  cameraPos.y,  atan2(cPos.x,  cPos.y)*180./M_PI );
	//MGlobal::displayInfo( buffer );

	//MPoint cameraPos = fnCamera.eyePoint ( MSpace::kWorld, &stat );
	//CHECKRESULT(stat,"MFnCamera Eye point failed!");
	//sprintf( buffer, "cam pos  %f %f %f", cameraPos.x,  cameraPos.y,  cameraPos.z );
	//MGlobal::displayInfo( buffer );


	return MS::kSuccess;
}


MStatus pfx2ParticlesCmd::printRenderLine( MRenderLine &renderLine, int lineType )
{
	char buffer[256];

	MVectorArray line = renderLine.getLine();
	MVectorArray twist = renderLine.getTwist();
	MDoubleArray width = renderLine.getWidth();
	MDoubleArray flatness = renderLine.getFlatness();
	MDoubleArray parameter = renderLine.getParameter();
	MVectorArray color = renderLine.getColor();
	MVectorArray incandescence = renderLine.getIncandescence();
	MVectorArray transparency = renderLine.getTransparency();

	if( line.length() > 0 &&  width.length() > 0){
	int i;
	int jj=0;
	double type=lineType;
	double R;
	double stampDen = pfxStampDensity;
	MVector vec1 = line[0];	
	MVector col1 = color[0];	
	MVector twi1 = twist[0];	
	MVector inc1 = incandescence[0];	
	MVector tra1 = transparency[0];	
	double fla1 = flatness[0];
	double par1 = parameter[0];
	double dot;
	//MVector view;
	//MVector vecCamSpace;
	MVector vec;
	MVector vec2;
	
			
	double w1 = width[0]/2.;
	int len = line.length()-1;
	
	if(useFlantess)	{
			dot = fabs(twi1.normal()*(vec1 - cameraPos).normal());
			if(dot <0.001) dot=0.001;
			w1*=(1+( dot - 1 )*fla1);
			}
	
	double w2;
	short x1, x2, y1, y2;
	MStatus st;
	double L;
	double origL=1;
	//double sumL=0;
	//double sumLreal=0;
	for( i = 0; i < len; i++ ){ // through the line
			
		vec2 = line[i+1];
		
		//w1 = width[i]/2.; //original behaviour
		origL= 1;//(line[i+1]-line[i]).length() + (line[i]-vec1).length() / (vec2-vec1).length();//original behaviour

		
		
		MVector col2 = color[i+1];	
		MVector twi2 = twist[i+1];	
		MVector inc2 = incandescence[i+1];	
		MVector tra2 = transparency[i+1];	
		double fla2 = flatness[i+1];
		double par2 = parameter[i+1];

		
		vec = vec2-vec1;

		w2 = width[i+1]/2.;
		
	if(useFlantess)	{
			dot = fabs(twi2.normal()*(vec2 - cameraPos).normal());
			if(dot <0.001) dot=0.001;
			w2*=(1+( dot - 1 )*fla2);
			}

		double dw = w1-w2;
		L =  vec.length()*stampDen*origL ;
		
		R = w1;
		
		//uDelta
		
	
	//sprintf( buffer, "jj=%d typ=%f par= %f  w1=%f w2= %f L=%f vec1=(%f,%f,%f)", jj, type, par1, w1, w2, L, vec1.x,vec1.y,vec1.z);	
	//MGlobal::displayInfo( buffer );	
		while (1) {

			if(w1<radiusThreshold) w1 = radiusThreshold;
			if(L+dw !=0 ) 	R = w1 * (L-dw)/(L+dw);
			else R = 0;
//		sprintf( buffer, " i=%d w1=%f w2=%f  dw=%f w1_R= %f R= %f L=%f vec1=(%f,%f,%f) vec2=(%f,%f,%f)", i, w1, w2, (w1-w2), (w1+R), (R), L, vec1.x,vec1.y,vec1.z, vec2.x,vec2.y,vec2.z);	
//		MGlobal::displayInfo( buffer );	
			if( R <= radiusThreshold ) { 
				w1=radiusThreshold;vec1 = vec2; 
//				sprintf( buffer, "-i=%d w1=%f w2=%f  dw=%f w1_R= %f R= %f L=%f vec1=(%f,%f,%f) vec2=(%f,%f,%f)", i, w1, w2, (w1-w2), (w1+R), (R), L, vec1.x,vec1.y,vec1.z, vec2.x,vec2.y,vec2.z);	
//				MGlobal::displayInfo( buffer );	

				break; }//break because flatnessmess instead of return MS::kSuccess;  //R = 0.001;
			
			//flatness mess
			double w1_R = w1 + R;
			if( w1_R >= L )  { 
//								sprintf( buffer, "+i=%d w1=%f w2=%f  dw=%f w1_R= %f R= %f L=%f vec1=(%f,%f,%f) vec2=(%f,%f,%f)", i, w1, w2, (w1-w2), (w1+R), (R), L, vec1.x,vec1.y,vec1.z, vec2.x,vec2.y,vec2.z);	
//				MGlobal::displayInfo( buffer );	

				break; } 
			posArray.append ((vec1)); // posArray.append (vec1.y);posArray.append (vec1.z); //0 1 2 +0
			colArray.append (col1); //result.append (col1.y);result.append (col1.z); //3 4 5 +3
			twiArray.append (twi1); //result.append (twi1.y);result.append (twi1.z); //6 7 8 
			incArray.append (inc1);//result.append (inc1.y);result.append (inc1.z); //9 10 11 
			traArray.append (tra1);//result.append (tra1.y);result.append (tra1.z); //12 13 14
			opaArray.append (1-tra1.x);//result.append (tra1.y);result.append (tra1.z); //12 13 14
			widArray.append(w1); //15
			flaArray.append(fla1); //16
			parArray.append(par1); //17        
			typArray.append(type); //17        
			if(spriteSupport) {//vecCamSpace = vec * cameraMatrix;
					M3dView::active3dView().worldToView( MPoint(vec1), x1, y1, &st) ;	
					M3dView::active3dView().worldToView( MPoint(vec2), x2, y2, &st) ;
					rotArray.append(atan2(float(y2-y1), float(x2-x1) )*180./M_PI - 90.); //18        19 total
					}
			if(velocityOutput) velArray.append(vec); //17        
			uDeltaArray.append(fabs(par2-par1)*w1/L); // uDelta         
			


			vec1 = vec1 + vec*(w1_R/(L));
			col1 = col1 + (col2-col1)*(w1_R/L);
			twi1 = twi1 + (twi2-twi1)*(w1_R/L);
			inc1 = inc1 + (inc2-inc1)*(w1_R/L);
			tra1 = tra1 + (tra2-tra1)*(w1_R/L);
			fla1 = fla1 + (fla2-fla1)*(w1_R/L);
			par1 = par1 + (par2-par1)*(w1_R/L);
			
			w1 = R;
			vec = vec2-vec1;
			L =  vec.length()*stampDen*origL;
			dw = w1-w2;
			if (jj++>10000) { 
				//MGlobal::displayInfo( "more than 100  stamps on one line" ); 
	
sprintf( buffer, "i=%d\tjj=%d\tR/w1=%f w2= %f L=%f dw=%f vec=(%f,%f,%f)", i, jj, w1, w2, L, dw, L, vec.x,vec.y,vec.z);	
MGlobal::displayInfo( buffer );
				return MS::kSuccess; 
				
				}
			} //end while next
		} //end for
		
		posArray.append ((vec1)); // posArray.append (vec1.y);posArray.append (vec1.z); //0 1 2 +0
		colArray.append (col1); //result.append (col1.y);result.append (col1.z); //3 4 5 +3
		twiArray.append (twi1); //result.append (twi1.y);result.append (twi1.z); //6 7 8 
		incArray.append (inc1);//result.append (inc1.y);result.append (inc1.z); //9 10 11 
		traArray.append (tra1);//result.append (tra1.y);result.append (tra1.z); //12 13 14
		opaArray.append (1-tra1.x);//result.append (tra1.y);result.append (tra1.z); //12 13 14
		widArray.append(w1); //15
		flaArray.append(fla1); //16
		parArray.append(par1); //17        
		typArray.append(type); //18        19 total
		M3dView::active3dView().worldToView( MPoint(vec1), x1, y1, &st) ;	
		M3dView::active3dView().worldToView( MPoint(vec2), x2, y2, &st) ;
		if(spriteSupport) rotArray.append(atan2(float(y2-y1), float(x2-x1))*180./M_PI - 90.); //18        19 total
		if(velocityOutput) velArray.append(vec); //17        
		uDeltaArray.append(fabs(parameter[len]-par1)*w1/L); // uDelta         
				
		posArray.append (line[len]); // posArray.append (vec1.y);posArray.append (vec1.z); //0 1 2 +0
		colArray.append (color[len]); //result.append (col1.y);result.append (col1.z); //3 4 5 +3
		twiArray.append (twist[len]); //result.append (twi1.y);result.append (twi1.z); //6 7 8 
		incArray.append (incandescence[len]);//result.append (inc1.y);result.append (inc1.z); //9 10 11 
		traArray.append (transparency[len]);//result.append (tra1.y);result.append (tra1.z); //12 13 14
		opaArray.append (1-transparency[len].x);//result.append (tra1.y);result.append (tra1.z); //12 13 14
		widArray.append(w2); //15 ??
		flaArray.append(flatness[len]); //16
		parArray.append(parameter[len]); //17        
		typArray.append(type); //18        19 total
		if(spriteSupport) rotArray.append(atan2(float(y2-y1), float(x2-x1))*180./M_PI - 90.); //18        19 total
		if(velocityOutput) velArray.append(vec); //17        
		uDeltaArray.append(fabs(parameter[len]-par1)*w2/L); // uDelta         
			
	} //endif


	return MS::kSuccess;
}

MStatus pfx2ParticlesCmd::printRenderLineArray( MRenderLineArray &lineArray, int lineType )
{
	//char buffer[256];

	int length = lineArray.length();
	int type;
	MStatus stat = MS::kSuccess;
	for( int i = 0; i < length; i++ ){
		MRenderLine renderLine = lineArray.renderLine( i, &stat );
		if(i==0 && lineType==0) type=-1; //mast found!
		else type=lineType;
		if( stat == MS::kSuccess ){
			printRenderLine( renderLine, type );
		}
	} 
	return MS::kSuccess;
}

//
// Main routine
///////////////////////////////////////////////////////////////////////////////
MStatus pfx2ParticlesCmd::doIt( const MArgList& args )
{
	char buffer[256];
	MStatus stat = parseArgs( args );
	if( stat != MS::kSuccess ) {
		return stat;
	}
	
	MFnPfxGeometry node( pfxNode );

	MRenderLineArray mainLines, leafLines, flowerLines;

	
		stat = node.getLineData( mainLines, leafLines, flowerLines,
		true, true, true, true,
		true, true, true, true, true );
	
	if( stat != MS::kSuccess ){
		return stat;
	}

	stat = printRenderLineArray( mainLines, 0 );
	if( stat != MS::kSuccess ){
		return stat;
	}

	stat = printRenderLineArray( leafLines, 1 );
	if( stat != MS::kSuccess ){
		return stat;
	}

	stat = printRenderLineArray( flowerLines, 2 );
	if( stat != MS::kSuccess ){
		return stat;
	}
	// If getLineData was successfully called then at some point
	// the arrays create for it must be deleted. Delete array
	// will remove all memory internally created for the renderLineArray.
	// Be careful: If you have any MRenderLines or arrays that were returned by this class,
	// you should not access them after calling DeleteArray. The internal
	// wrapped data is currently copied by pointer, with no reference counting. 
	// Thus if you assign one MRenderLineArray to another, you should only call
	// the deleteArray once( and not access the other MRenderLineArray after doing so ).
	//clearResult(); 
    //setResult( result );
	int pfxSize = posArray.length();

if(pfxOutput != 0 )
	{
	//MFnDependencyNode fnDN(particleNode);
	if( ! (particleNode.hasFn( MFn::kParticle ) ) )
	{
		MGlobal::displayError( "The named node is not a particleNode." );
		return MS::kFailure;
	}
	//checking particle type
	MFnParticleSystem ps( particleNode, &stat );
	CHECKRESULT(stat,"MFnParticleSystem::MFnParticleSystem(MObject,status) failed!");
	if( ! ps.isValid() )
	{
		MGlobal::displayError( "The particleNode function set is invalid!" );
		return MS::kFailure;
	}

	MObject attr = ps.attribute("inputGeometry",&stat);
	
	CHECKRESULT(stat,"attr failed!");
	//get plug for nurbs curve
    MPlug attrPlug = MPlug(  particleNode , attr);
	
	//getting count
	
	//COUNT
	int particleCount = ps.count();
	
	// building curve
	unsigned	ncvs = pfxSize;			// Number of CVs
/*	 if(pfxSize == particleCount && pfxSize > 0) 
		{
		ncvs=pfxSize-1; // this is hack for updating nurbse curve attribute - to change num of cV
		posArray.setLength(ncvs);
		colArray.setLength(ncvs);
		twiArray.setLength(ncvs);
		traArray.setLength(ncvs);
		incArray.setLength(ncvs);
		widArray.setLength(ncvs);
		flaArray.setLength(ncvs);
		typArray.setLength(ncvs);
		opaArray.setLength(ncvs);
		parArray.setLength(ncvs);
		}
*/		
	unsigned	i;

	MPointArray	 controlVertices;
	MDoubleArray knotSequences;


	for (i = 0; i < ncvs; i++) {
		controlVertices.append( MPoint( posArray[i].x , posArray[i].y, posArray[i].z ) );
		knotSequences.append( (double)i );
		if(useWidthMultiplication) widArray[i]*=pfxRadius;
		}
	// Now create the curve
	//
	
	MFnNurbsCurve curveFn;
	
	MFnNurbsCurveData dataCreator;
	MObject outCurveData = dataCreator.create();

	MObject outputCurve   = curveFn.create( controlVertices,
					knotSequences, 1, 
					MFnNurbsCurve::kOpen, 
					false, false, 
					outCurveData, 
					&stat );					
	/*	MObject outputCurve  = curveFn.createWithEditPoints( controlVertices,  //Ultra slow!
					1, 
					MFnNurbsCurve::kOpen, 
					false, false, 1, 
					outCurveData, 
					&stat );
					*/
	CHECKRESULT(stat,"zeroed stroke - createCurve(MObject) failed!");
	
	//setting .igeo attr! updating particle pos
	stat = attrPlug.setMObject(outCurveData);
	CHECKRESULT(stat,"setMObject(MObject) failed!");
	// curve created! particle updated!
	
/*
//sprintf( buffer, "count=%d : del= %d", count, pfxDelta);
//MGlobal::displayInfo( buffer );

	if(pfxDelta>0) { //need to emit
		MPointArray emitPoints(pfxDelta,MPoint(0, 100, 0)); 
		stat = ps.emit(emitPoints);
		CHECKRESULT(stat,"MFnParticleSystem::emit(pos) failed!");
	}
	if(pfxDelta<0) { // need to hide
		int ii;
		for(ii=0;ii<-pfxDelta;ii++)
			{
			//MVectorArray hidePoints(-pfxDelta,MVector(0, 5, 0)); 
			stat = posArray.append(MVector(0, 100, 0));
			//stat = colArray.append(MVector(0, 0, 0));
			stat = widArray.append(0.0);
			CHECKRESULT(stat,"MFnParticleSystem::append countß∫ failed!");
			}
	}


//	MVectorArray currentPos;
//	MString currentPosName("position");
//	ps.getPerParticleAttribute( currentPosName, currentPos, &stat );
//		CHECKRESULT(stat,"MFnParticleSystem::ps.position failed!");

	//posArray.setLength(count);

//sprintf( buffer, "-pfxPos472[1]=%f", posArray[472][1]);
//MGlobal::displayInfo( buffer );

//	ps.setPerParticleAttribute( currentPosName, currentPos, &stat );
*/

	if(pfxOutput>0){
		if(pfxSize == particleCount && pfxSize > 0) ps.setPerParticleAttribute( MString("position"), posArray, &stat );	//setAttr .igeo hack
		if(velocityOutput) ps.setPerParticleAttribute( MString("velocity"), velArray, &stat );
		ps.setPerParticleAttribute( MString("rgbPP"), colArray, &stat );
		ps.setPerParticleAttribute( MString("radiusPP"), widArray, &stat );
		ps.setPerParticleAttribute( MString("opacityPP"), opaArray, &stat );
		ps.setPerParticleAttribute( MString("incandescencePP"), incArray, &stat );
		if(spriteSupport) {
			ps.setPerParticleAttribute( MString("spriteScaleXPP"), widArray, &stat );
			ps.setPerParticleAttribute( MString("spriteScaleYPP"), widArray, &stat );
			ps.setPerParticleAttribute( MString("spriteTwistPP"), rotArray, &stat );
			}
		}
	/*
	ps.setPerParticleAttribute( MString("pfxStampPP"), posArray, &stat );		
	ps.setPerParticleAttribute( MString("pfxrgbPP"), colArray, &stat );
	ps.setPerParticleAttribute( MString("pfxradiusPP"), widArray, &stat );
	ps.setPerParticleAttribute( MString("pfxtransparencyPP"), traArray, &stat );
	ps.setPerParticleAttribute( MString("pfxincandescencePP"), incArray, &stat );
	*/
	if(!shortOutput) {
		ps.setPerParticleAttribute( MString("pfxNormalPP"), twiArray, &stat );
		ps.setPerParticleAttribute( MString("pfxFlatnessPP"), flaArray, &stat );
		ps.setPerParticleAttribute( MString("pfxParameterPP"), parArray, &stat );
		ps.setPerParticleAttribute( MString("pfxDeltaUPP"), uDeltaArray, &stat );
		ps.setPerParticleAttribute( MString("pfxTypePP"), typArray, &stat );
		}
	
	if(saveInitialState) ps.saveInitialState();
	} //endif(pfxOutput!=0){


	clearResult(); 
    setResult( pfxSize );
	
	mainLines.deleteArray();
	leafLines.deleteArray();
	flowerLines.deleteArray();

	return MS::kSuccess;
}


//
//
///////////////////////////////////////////////////////////////////////////////

void * pfx2ParticlesCmd::creator() { return new pfx2ParticlesCmd(); }


MStatus initializePlugin( MObject obj )
{
    MFnPlugin plugin( obj, "Sergey Tsyptsyn & Paolo Berto, /*jupiter jazz*/", "11.5", "Any");

    MStatus status = plugin.registerCommand(mCommandName,
											pfx2ParticlesCmd::creator, pfx2ParticlesCmd::newSyntax );
	if (!status) 
		status.perror("registerCommand");

    return status;
}


MStatus uninitializePlugin( MObject obj )
{
    MFnPlugin plugin( obj );

    MStatus status = plugin.deregisterCommand(mCommandName);
	if (!status) 
		status.perror("deregisterCommand");

    return status;
}
